package com.joker.airoleplayplatformback.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.joker.airoleplayplatformback.domain.enums.KnowledgeBaseEnum;
import com.joker.airoleplayplatformback.domain.po.KnowledgeBasePO;
import com.joker.airoleplayplatformback.domain.po.UserPO;
import com.joker.airoleplayplatformback.mapper.KnowledgeBaseMapper;
import com.joker.airoleplayplatformback.service.KnowledgeBaseService;
import com.joker.airoleplayplatformback.service.OssService;
import com.joker.airoleplayplatformback.service.UserService;
import com.joker.airoleplayplatformback.utils.DocumentParserUtils;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.filter.Filter;
import io.qdrant.client.grpc.Points;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;

/**
 * 知识库服务实现类
 *
 * @author feixiang.li
 * @since 2025-09-27
 */
@Slf4j
@Service
public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {

    @Autowired
    private KnowledgeBaseMapper knowledgeBaseMapper;

    @Autowired
    private EmbeddingModel embeddingModel;

    @Autowired
    private UserService userService;

    @Value("${qdrant.host}")
    private String qdrantHost;
    @Value("${qdrant.port}")
    private int qdrantPort;

    @Autowired
    EmbeddingStore<TextSegment> embeddingStore;


    @Autowired
    private OssService ossService;

    @Autowired
    @Qualifier("taskExecutor")
    Executor executor;

    @Override
    public KnowledgeBasePO uploadDocument(Long userId, String name, String description, MultipartFile file) {
        try {
            // 解析文档内容
            String content = DocumentParserUtils.parseDocument(file);

            // 上传文件到OSS
            String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
            String fileUrl = ossService.uploadFile(file, "knowledge/" + fileName);

            // 创建知识库记录
            KnowledgeBasePO knowledgeBase = new KnowledgeBasePO();
            knowledgeBase.setUserId(userId);
            knowledgeBase.setName(name);
            knowledgeBase.setDescription(description);
            knowledgeBase.setFileType(DocumentParserUtils.getFileType(file.getOriginalFilename()));
            knowledgeBase.setOriginalFileName(file.getOriginalFilename());
            knowledgeBase.setFileUrl(fileUrl);
            knowledgeBase.setContent(content);
            knowledgeBase.setFileSize(file.getSize());
            knowledgeBase.setStatus(0);

            knowledgeBaseMapper.insert(knowledgeBase);
            log.info("文档上传成功: userId={}, fileName={}", userId, file.getOriginalFilename());

            // 异步执行文档的解析任务
            executor.execute(() -> {
                uploadQdrant(knowledgeBase);
            });
            return knowledgeBase;
        } catch (Exception e) {
            log.error("文档上传失败", e);
            throw new RuntimeException("文档上传失败: " + e.getMessage());
        }
    }


    public void uploadQdrant(KnowledgeBasePO knowledgeBasePO) {
        try {

            Long userId = knowledgeBasePO.getUserId();
            UserPO user = userService.findById(userId);

            Document document = Document.from(knowledgeBasePO.getContent());

            // 分割文档
            DocumentSplitter documentSplitter = DocumentSplitters.recursive(500, 100);
            List<TextSegment> segments = documentSplitter.split(document);

            // 处理每个文本片段
            for (TextSegment segment : segments) {
                segment.metadata().put("fileName",
                        StringUtils.isNotBlank(knowledgeBasePO.getOriginalFileName()) ?
                                knowledgeBasePO.getOriginalFileName() : "文本");
                segment.metadata().put("userId", userId);

                Response<Embedding> embed = embeddingModel.embed(segment);
                embeddingStore.add(embed.content(), segment);
                log.info("上传Qdrant成功: userId={}, fileName={}", userId, knowledgeBasePO.getName());
            }

            // 更新知识库状态为成功
            knowledgeBasePO.setStatus(KnowledgeBaseEnum.SUCCESS.getCode());
            knowledgeBaseMapper.updateById(knowledgeBasePO);

        } catch (Exception e) {
            log.error("知识库上传Qdrant失败", e);
            knowledgeBasePO.setStatus(KnowledgeBaseEnum.ERROR.getCode());
            knowledgeBaseMapper.updateById(knowledgeBasePO);
        } finally {
        }
    }

    /**
     * 根据 userId 和 query 进行搜索
     *
     * @param userId 用户ID
     * @param query  查询
     * @return 搜索结果
     */
    @Override
    public List<String> search(Long userId, String query) {
        Embedding queryEmbedding = embeddingModel.embed(query).content();
        EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
                .queryEmbedding(queryEmbedding)
                .filter(metadataKey("userId").isEqualTo(userId))
                .maxResults(10)
                .build();
        try {
            EmbeddingSearchResult<TextSegment> search = embeddingStore.search(embeddingSearchRequest);
            if (Objects.isNull(search) || CollUtil.isEmpty(search.matches())) {
                return Collections.emptyList();
            }
            return search.matches().stream().map(match -> match.embedded().text()).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("知识库搜索失败", e);
        }
        return Collections.emptyList();
    }

    /**
     * 下载文件到临时目录
     */
    private String downloadFile(KnowledgeBasePO knowledgeBasePO) throws IOException {
        // 假设knowledgeBasePO中包含文件URL或文件存储路径
        String fileUrl = knowledgeBasePO.getFileUrl();
        String originalFileName = knowledgeBasePO.getOriginalFileName();

        if (StringUtils.isBlank(fileUrl)) {
            throw new IllegalArgumentException("文件URL不能为空");
        }

        // 创建临时目录
        String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "qdrant_uploads";
        File dir = new File(tempDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 生成临时文件名
        String fileExtension = getFileExtension(originalFileName);
        String tempFileName = "file_" + System.currentTimeMillis() + "_" +
                knowledgeBasePO.getId() + fileExtension;
        String filePath = tempDir + File.separator + tempFileName;

        // 下载文件
        try (InputStream in = new URL(fileUrl).openStream();
             FileOutputStream out = new FileOutputStream(filePath)) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }

        log.info("文件下载完成: {}", filePath);
        return filePath;
    }

    /**
     * 将文本内容保存到临时文件
     */
    private String saveTextToTempFile(KnowledgeBasePO knowledgeBasePO) throws IOException {
        String content = knowledgeBasePO.getContent();
        if (StringUtils.isBlank(content)) {
            throw new IllegalArgumentException("文本内容不能为空");
        }

        // 创建临时目录
        String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "qdrant_uploads";
        File dir = new File(tempDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 创建临时文件
        String tempFileName = "text_" + System.currentTimeMillis() + "_" +
                knowledgeBasePO.getId() + ".txt";
        String filePath = tempDir + File.separator + tempFileName;

        // 写入文本内容
        try (FileWriter writer = new FileWriter(filePath)) {
            writer.write(content);
        }

        log.info("文本保存到临时文件: {}", filePath);
        return filePath;
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }
        int lastDotIndex = fileName.lastIndexOf('.');
        return (lastDotIndex > 0) ? fileName.substring(lastDotIndex) : "";
    }

    /**
     * 清理临时文件
     */
    private void cleanupTempFile(String filePath) {
        try {
            File file = new File(filePath);
            if (file.exists()) {
                boolean deleted = file.delete();
                if (deleted) {
                    log.info("临时文件已删除: {}", filePath);
                } else {
                    log.warn("临时文件删除失败: {}", filePath);
                }
            }
        } catch (Exception e) {
            log.warn("删除临时文件失败: {}", filePath, e);
        }
    }


    @Override
    public KnowledgeBasePO uploadText(Long userId, String name, String description, String content) {
        try {
            KnowledgeBasePO knowledgeBasePO = new KnowledgeBasePO();
            knowledgeBasePO.setUserId(userId);
            knowledgeBasePO.setName(name);
            knowledgeBasePO.setDescription(description);
            knowledgeBasePO.setFileType("text");
            knowledgeBasePO.setContent(content);
            knowledgeBasePO.setFileSize((long) content.getBytes().length);
            knowledgeBasePO.setStatus(0);

            knowledgeBaseMapper.insert(knowledgeBasePO);
            log.info("文本上传成功: userId={}, name={}", userId, name);

            return knowledgeBasePO;
        } catch (Exception e) {
            log.error("文本上传失败", e);
            throw new RuntimeException("文本上传失败: " + e.getMessage());
        }
    }

    @Override
    public List<KnowledgeBasePO> getUserKnowledgeBases(Long userId) {
        LambdaQueryWrapper<KnowledgeBasePO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(KnowledgeBasePO::getUserId, userId).orderByDesc(KnowledgeBasePO::getCreateTime);
        return knowledgeBaseMapper.selectList(queryWrapper);
    }

    @Override
    public KnowledgeBasePO getKnowledgeBaseById(Long id) {
        return knowledgeBaseMapper.selectById(id);
    }

    @Override
    public boolean deleteKnowledgeBase(Long id, Long userId) {
        int result = knowledgeBaseMapper.delete(new LambdaQueryWrapper<KnowledgeBasePO>().eq(KnowledgeBasePO::getId, id).eq(KnowledgeBasePO::getUserId, userId));
        log.info("删除知识库: id={}, userId={}, result={}", id, userId, result > 0);
        return result > 0;
    }

    @Override
    public KnowledgeBasePO updateKnowledgeBase(KnowledgeBasePO knowledgeBasePO) {
        knowledgeBaseMapper.updateById(knowledgeBasePO);
        return knowledgeBasePO;
    }
}
